package com.bt.plugin.idea.mybatisx.alias;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.plugin.idea.mybatisx.alias.PackageAliasResolver;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;

/**
 * @author duanxiuqing
 * @date 2025/5/11
 */
public class BtMybatisResolver extends PackageAliasResolver {

    private static final List<String> MAPPER_ALIAS_PACKAGE_CLASSES = new ArrayList<String>() {
        {
            // default
            add("org.mybatis.spring.SqlSessionFactoryBean");
            // mybatis-plus3
            add("com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean");
            // mybatis-plus2
            add("com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean");
        }
    };

    public BtMybatisResolver(Project project) {
        super(project);
    }

    private  static Optional<Collection<String>> result = Optional.empty();

    @Override
    public Collection<String> getPackages(PsiElement psiElement) {
        if (result.isEmpty()) {
            Collection<String> strings = detectTypeAliasesPackages(project);
            strings = strings.stream().map(e -> {
                if (e.endsWith(".*")) {
                    return e;
                }
                // mybatisx中的bug，需要加上.*号，不然识别不出来
                return e + ".*";
            }).collect(Collectors.toList());
            result = Optional.of(strings);
            return strings;
        }
        return result.get();
    }

    public static Collection<String> detectTypeAliasesPackages(@NotNull Project project) {
        Set<String> results = new HashSet<>();
        GlobalSearchScope scope = GlobalSearchScope.allScope(project);

        // 查找所有配置类
        for (String className : MAPPER_ALIAS_PACKAGE_CLASSES) {
            PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, scope);
            if (psiClass == null) continue;

            // 查找类的所有 setTypeAliasesPackage 方法
            PsiMethod[] methods = psiClass.findMethodsByName("setTypeAliasesPackage", false);
            for (PsiMethod method : methods) {
                // 查找方法的所有调用
                for (final PsiReference reference : ReferencesSearch.search(method)) {
                    PsiElement element = reference.getElement();
                    PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
                    if (call != null) {
                        // 检查参数
                        PsiExpression[] args = call.getArgumentList().getExpressions();
                        if (args.length > 0) {
                            String value = extractStringValue(args[0]);
                            if (value != null) {
                                results.add(value);
                            }
                        }
                    }
                }
            }
        }

        return results;
    }

    private static String extractStringValue(PsiExpression expression) {
        if (expression instanceof PsiLiteralExpression) {
            Object value = ((PsiLiteralExpression) expression).getValue();
            return value instanceof String ? (String) value : null;
        }
        if (expression instanceof PsiReferenceExpression) {
            PsiElement resolved = ((PsiReferenceExpression) expression).resolve();
            if (resolved instanceof PsiField && ((PsiField) resolved).hasModifierProperty(PsiModifier.STATIC)) {
                Object value = ((PsiField) resolved).computeConstantValue();
                return value instanceof String ? (String) value : null;
            }
        }
        return null;
    }

}
